{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 修饰符的使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## @classmethod 修饰符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "在 `Python` 标准库中，有很多自带的修饰符，例如 `classmethod` 将一个对象方法转换了类方法： "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Foo(object):\n",
    "    @classmethod\n",
    "    def bar(cls, x):\n",
    "        print 'the input is', x\n",
    "        \n",
    "    def __init__(self):\n",
    "        pass\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类方法可以通过 `类名.方法` 来调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the input is 12\n"
     ]
    }
   ],
   "source": [
    "Foo.bar(12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## @property 修饰符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时候，我们希望像 __Java__ 一样支持 `getters` 和 `setters` 的方法，这时候就可以使用 `property` 修饰符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Foo(object):\n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "    \n",
    "    @property\n",
    "    def x(self):\n",
    "        return self.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此时可以使用 `.x` 这个属性查看数据（不需要加上括号）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "23"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo = Foo(23)\n",
    "foo.x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样做的好处在于，这个属性是只读的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "can't set attribute",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-5-e5e7e6c675ef>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mfoo\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: can't set attribute"
     ]
    }
   ],
   "source": [
    "foo.x = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果想让它变成可读写，可以加上一个修饰符 `@x.setter`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Foo(object):\n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "    \n",
    "    @property\n",
    "    def x(self):\n",
    "        return self.data\n",
    "    \n",
    "    @x.setter\n",
    "    def x(self, value):\n",
    "        self.data = value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "23\n"
     ]
    }
   ],
   "source": [
    "foo = Foo(23)\n",
    "print foo.x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以通过属性改变它的值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "foo.x = 1\n",
    "print foo.x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Numpy 的 @vectorize 修饰符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`numpy` 的 `vectorize` 函数讲一个函数转换为 `ufunc`，事实上它也是一个修饰符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-10.,  -9.,  -8.,  -7.,  -6.,  -5.,  -4.,  -3.,  -2.,  -1.,   0.,\n",
       "         0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from numpy import vectorize, arange\n",
    "\n",
    "@vectorize\n",
    "def f(x):\n",
    "    if x <= 0:\n",
    "        return x\n",
    "    else:\n",
    "        return 0\n",
    "\n",
    "f(arange(-10.0,10.0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 注册一个函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "来看这样的一个例子，定义一个类："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Registry(object):\n",
    "    def __init__(self):\n",
    "        self._data = {}\n",
    "    def register(self, f, name=None):\n",
    "        if name == None:\n",
    "            name = f.__name__\n",
    "        self._data[name] = f\n",
    "        setattr(self, name, f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`register` 方法接受一个函数，将这个函数名作为属性注册到对象中。\n",
    "\n",
    "产生该类的一个对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "registry = Registry()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用该对象的 `register` 方法作为修饰符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "@registry.register\n",
    "def greeting():\n",
    "    print \"hello world\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样这个函数就被注册到 `registry` 这个对象中去了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'greeting': <function __main__.greeting>}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "registry._data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.greeting>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "registry.greeting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[flask](flask.pocoo.org) ，一个常用的网络应用，处理 url 的机制跟这个类似。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用 @wraps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个通常的问题在于："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None wrapper\n"
     ]
    }
   ],
   "source": [
    "def logging_call(f):\n",
    "    def wrapper(*a, **kw):\n",
    "        print 'calling {}'.format(f.__name__)\n",
    "        return f(*a, **kw)\n",
    "    return wrapper\n",
    "\n",
    "@logging_call\n",
    "def square(x):\n",
    "    '''\n",
    "    square function.\n",
    "    '''\n",
    "    return x ** 2\n",
    "\n",
    "print square.__doc__, square.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们使用修饰符之后，`square` 的 `metadata` 完全丢失了，返回的函数名与函数的 `docstring` 都不对。\n",
    "\n",
    "一个解决的方法是从 `functools` 模块导入 `wraps` 修饰符来修饰我们的修饰符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    square function.\n",
      "     square\n"
     ]
    }
   ],
   "source": [
    "import functools\n",
    "\n",
    "def logging_call(f):\n",
    "    @functools.wraps(f)\n",
    "    def wrapper(*a, **kw):\n",
    "        print 'calling {}'.format(f.__name__)\n",
    "        return f(*a, **kw)\n",
    "    return wrapper\n",
    "\n",
    "@logging_call\n",
    "def square(x):\n",
    "    '''\n",
    "    square function.\n",
    "    '''\n",
    "    return x ** 2\n",
    "\n",
    "print square.__doc__, square.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在这个问题解决了，所以在自定义修饰符方法的时候为了避免出现不必要的麻烦，尽量使用 `wraps` 来修饰修饰符！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Class 修饰符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与函数修饰符类似，类修饰符是这样一类函数，接受一个类作为参数，通常返回一个新的类。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
